A brief look at Der Langrisser data structures and offsets By Derrick Sobodash http://cinnamonpirate.com/ First published December 21, 2007 Last updated December 21, 2007 This work is hereby declared as part of the Public Domain. The author reserves no rights. ------------------------------------------------------------------------------- INTRODUCTION I decided to write this to track my tweaking of Der Langrisser's internal structs. For years people asked about making a "hard mode" of Der Langrisser, so this seemed like a great way to help everyone out. I first found the tables while using hacking notes a Chinese netizen "maxmat" posted to the Emumax gaming forum for Der Langrisser. He made a few patches to make early classes get super units, and I intended to use this to solve the problem of Elementals as non-hireable units. Then I kept poking around, and based on his patch and a few others floating around, located the bulk of the data structures. I had one thing to help figure out what every entry is that maxmat did not: the scripts. So here, I present the data structures as best I understand them, along with the arrays to fill in all the values so they make sense. If you are interested, you can view these structs as dumped to spreadsheets at my Google Docs account: http://spreadsheets.google.com/ccc?key=pi_0ICQyQm8QTcTx_5GQbSw&hl=en You will need a Google account or Gmail account to get in. Anyone who can help to fill in the last missing values, please do! I am also looking for a table to control class changes, and possible to control the stats of NPC and Enemy units in each scenario. This could be in the compressed scenario scription blocks which control dialogue and turns. ------------------------------------------------------------------------------- CHARACTER BASE STATS These are the stats each playable character begins with before he/she/it starts gaining experience. Struct entries are each 16 bytes long. There are 18 entries which correspond to the 18 entries in the array COMMANDER_NAMES. Start Offset: 0x016ce0 struct BASE_STATS { int8 AT, int8 DF, int24 Spells, /* Bit string where turns entries in magic array on/off */ bool Summon, /* Store as int8, 0=off/1=on */ int8 UNKNOWN1, int8 Portrait, int8 Troops, /* Number of troops hireable */ int8 Unit 1, /* From CLASS_NAMES array */ int8 Unit 2, int8 Unit 3, int32 UNKNOWN2 } Regarding the Spells entry. It works like this. If you had the big Endian value 0x800200, that is 100000000000001000000000 in binary. However, the bits of each byte are mirrored left to right. This should be interpreted to be: 000000010100000000000000. Laying this over the SPELL_NAMES array, it will turn on the spells Heal 1 and Turn Undead. ------------------------------------------------------------------------------- ITEM STATS Each item is able to have four properties assigned to it. The four can be mixed and matched from the array ITEM_PROPS. The following value is a signed 8-bit integer to offset that property. In the case of Summon, it pulls the spell name from the array SUMMONS. The struct contains 36 entries which correspond to the array ITEM_NAMES. Each entry is 8 bytes. Start Offset: 0x014fb1 struct ITEM_STATS { int8 Property1, /* From ITEM_PROPS */ int8 Value1, int8 Property2, /* From ITEM_PROPS */ int8 Value2, int8 Property3, /* From ITEM_PROPS */ int8 Value3, int8 Property4, /* From ITEM_PROPS */ int8 Value4 } ------------------------------------------------------------------------------- CLASS STATS These are the statistics used to determine what base values a class has. In the case of a class change, certain stats will be added cumulatively to the character's base: for example, AT/DF/A/D/MP. You will notice certain stats are far higher in hireable units, because you cannot change to classes such as Lizard Man. There are 255 entries corresponding to the game's class index table stored in array CLASS_NAMES. Each entry is 26 bytes. Start Offset: 0x036240 struct CLASS_STATS { int8 0x0, bool Flying, /* Determines true flying status. Battle screen y placement? */ int8 0x0, uint8 Type, /* from UNIT_TYPES */ uint8 Tier, int8 AT, int8 DF, int8 MV, int8 Range, /* Command range */ int8 A, int8 D, uint8 MP, int8 MagicDef, int8 Troops, uint8 Cost, /* Cost to hire in P. Multiply by 10 */ int32 UNKNOWN, uint8 Unit1, /* From CLASS_NAMES array */ uint8 Unit2, uint8 Unit3, uint8 Spell1, /* From SPELL_NAMES array */ uint8 Spell2, uint8 Spell3, uint8 Spell4, } ------------------------------------------------------------------------------- WINDOW COLORS While not a data structure, some people may wish to experiment with these. These values will only work in a Japanese Der Langrisser ROM. In the English version, we hijacked these values to store them elsewhere because of the new color switch option. The dialogue windows use what is called Color Add/Sub, not alpha blending. What it does is add or subtract a fixed amount of each color from each pixel within the field. Color Add will make things brighter, and Color Subtract will make them darker. You can't have it both ways. Offset: 0x0042d4 Possible Values { 0x03 = Color Add 0x83 = Color Subtract } The actual color additions and subtractions are stored a little further in. Offset: 0x00437a struct COLOR_TABLE { int8 AllyColor1, int8 AllyColor2, int8 AllyColor3, int8 NPCColor1, int8 NPCColor2, int8 NPCColor3, int8 EnemyColor1, int8 EnemyColor2, int8 EnemyColor3 } These color entries are SFC colors. For example, the default Ally window colors are 0x30 0x50 0x81, or binary 001 10000, 010 10000 and 100 00001. In the SFC Add/Sub, colors follow a pattern of bgriiiii. The first three bits are a toggle saying whether to modify Blue (100), Green (010) or Red (001). The last 5 bits are a value to offset by, giving you a possibility of 00000 to 11111, or 32 levels. The last value is the background color used in all non-dialogue windows. This is a 16-bit color. Offset: 0x007ba5 The default value is the little Endian word 0x1800 (stored in the ROM as 00 18), or 0001100000000000 in binary. The format for any SFC palette color is xbbbbbgggggrrrrr, where red, green and blue are 5 bits each. Using the above example, we have a value of rgb(0, 0, 6). ------------------------------------------------------------------------------- ENABLE THE BATTLE TEST The Battle Test was first discovered in alamone's Der Langrisser script dumps in 1998, and for years people scratched their heads over how to add it. The Der Langrisser English patch was the first time people could see the battle menu, and here is how it happened. Offset: 0x10e075 In the Japanese game, this value is 0x02. This is the number of options to show in the Media Test (from the load menu: Left, Right, Select A). All one needs to do is change this value to 0x03, and magically the Battle Test option will appear. Be warned, the Japanese one is nowhere near as refined as the English one, and the majority of options in it appear as total nonsense with random letters tacked onto the end. If you want a clear list of what each unit will be, you are better off using the English patch. byuu is the one who found this offset. ------------------------------------------------------------------------------- APPENDIX I static array UNIT_TYPES { string "Infantry", string "Infantry (Holy)", string "Spearmen", string "Cavalry", string "Cavalry (Mystic)", string "Flier", string "Infantry (Stealth)", string "Sailor", string "Gel", string "Demon", string "Sailor (Monster)", string "Infantry (Ranger)", string "Infantry (Mage)", string "Undead (Spirit)", string "Undead", string "Bowmen", string "Bowmen (Ballista)", string "Dragon" } static array ITEM_PROPS { string "AT", string "DF", string "MV", string "Range", string "A", string "D", string "Magic Range", string "Magic Damage", string "Magic Defense", string "Summon" } static array COMMANDER_NAMES { string "Erwin", string "Liana", string "Lana", string "Cherie", string "Hein", string "Scott", string "Keith", string "Aaron", string "Lester", string "Lana", /* Dark Princess */ string "Rohga", string "Sonya", string "Leon", string "Vargas", string "Imelda", string "Egbert", string "Esto", string "Osto" }; static array ITEM_NAMES { string "Not Equipped", string "Dagger", string "War Hammer", string "Long Sword", string "Magic Wand", string "Inferno Lance", string "Devil Axe", string "Dragon Slayer", string "Langrisser", string "Langrisser", /* Unsealed */ string "Iron Dumbbell", string "Masayan Sword", string "Orb", string "Holy Rod", string "Holy Rod", string "Dark Rod", string "Long Bow", string "Arbalest", string "Alhazard", string "Alhazard", /* Unsealed */ string "Odin's Buckler", string "Buckler", string "Shield", string "Chainmail", string "Platemail", string "Assault Suit", string "Cloak", string "Dragon Scale", string "Wizard's Robe", string "Amulet", string "Cross", string "Necklace", string "Swift Boots", string "Crown", string "Gleipnir", string "Rune Stone" } static array CLASS_NAMES { string " ", string "Fighter", /* Imperial */ string "Fighter", /* Light */ string "Fighter", string "Gladiator", /* Imperial */ string "Vampire", string "Knight", /* Imperial */ string "Knight", /* Light */ string "Pirate", /* Light */ string "Hawk Knight", /* Imperial */ string "Hawk Knight", /* Light */ string "Sister", string "Shaman", /* Imperial */ string "Warlock", /* Imperial */ string "Warlock", /* Light */ string "Werewolf", string "Gelgazer", string "Ghost", string "Scylla", string "Roc", string "Lord", /* Imperial */ string "Lord", /* Light */ string "Lord", string "Assassin", /* Imperial */ string "Assassin", /* Light */ string "Silver Knight", /* Imperial */ string "Silver Knight", /* Light */ string "Silver Knight", string "Captain", /* Imperial */ string "Captain", /* Light */ string "Hawk Lord", /* Imperial */ string "Hawk Lord", /* Light */ string "Cleric", string "Necromancer", /* Imperial */ string "Sorcerer", /* Imperial */ string "Sorcerer", /* Light */ string "Sorcerer", string "Paladin", string "Paladin", string "Kerberos", string "Dullahan", string "Lich", string "Serpent", string "Wyvern", string "High Lord", /* Imperial */ string "High Lord", /* Light */ string "High Lord", string "Swordsman", /* Imperial */ string "Swordsman", /* Light */ string "Highlander", /* Imperial */ string "Highlander", /* Light */ string "Highlander", string "Serpent Knight", string "Gladiator", string "Dragon Knight", /* Imperial */ string "Dragon Knight", /* Light */ string "Priest", string "Summoner", /* Imperial */ string "Mage", /* Imperial */ string "Mage", /* Light */ string "Mage", string "Saint", string "Saint", string "Unicorn Knight", /* Cherie */ string "Minotaur", string "Living Armour", string "Succubus", string "Kraken", string "Phoenix", string "General", /* Imperial */ string "General", /* Light */ string "General", string "Sword Master", /* Imperial */ string "Sword Master", /* Light */ string "Knight Master", /* Imperial */ string "Knight Master", /* Light */ string "Knight Master", string "Serpent Lord", /* Imperial */ string "Pirate", string "Dragon Lord", /* Chaos */ string "Dragon Lord", /* Light */ string "High Priest", string "Zauberer", /* Imperial */ string "Zauberer", /* Chaos */ string "Archmage", /* Imperial */ string "Archmage", /* Light */ string "Archmage", string "Sage", string "Sage", string "Ranger", /* Aaron */ string "Ranger", /* Rohga */ string "Master Dino", string "Stone Golem", string "Vampire Lord", string "Jormungandr", string "Great Dragon", string "King", string "Emperor", /* Bernhardt */ string "Hero", string "Hero", string "Hero", string "Queen", /* Imelda */ string "Royal Guard", /* Sonya */ string "Royal Guard", /* Leon */ string "Royal Guard", /* Scott */ string "Royal Guard", /* Erwin */ string "Serpent Master", /* Light */ string "Dragon Master", /* Light */ string "Agent", /* Liana */ string "Dark Master", /* Boser */ string "Dark Master", /* Egbert */ string "Wizard", /* Imperial */ string "Wizard", /* Imperial */ string "Wizard", /* Light */ string "Wizard", /* Light */ string "Princess", /* Cherie */ string "Sorcerer", /* Imperial */ string "High Master", /* Aaron */ string "High Master", /* Rohga */ string "Monk", string "Barbarian", string "Soldier", /* Imperial */ string "Soldier", /* Light */ string "Soldier", string "Berserker", string "Grenadier", /* Imperial */ string "Grenadier", /* Light */ string "Grenadier", string "Dark Guard", string "Lancer", string "Trooper", /* Light */ string "Trooper", string "Hell Hound", string "Royal Lancer", string "Dragoon", /* Light */ string "Dragoon", string "Bone Dino", string "Pike", /* Imperial */ string "Pike", /* Light */ string "Pike", string "Phalanx", /* Imperial */ string "Phalanx", /* Light */ string "Phalanx", string "Golem", string "Elf", /* Imperial */ string "Elf", /* Light */ string "Elf", string "Dark Elf", string "High Elf", /* Imperial */ string "High Elf", /* Light */ string "High Elf", string "Witch", string "Ballista", /* Chaos */ string "Ballista", /* Imperial */ string "Ballista", /* Light */ string "Ballista", string "Ghost", string "Spectre", string "Demon", string "Archdemon", string "Merman", /* Light */ string "Merman", string "Lizard Man", /* Chaos */ string "Lizard Man", /* Imperial */ string "Nixie", /* Light */ string "Nixie", string "Leviathan", /* Chaos */ string "Leviathan", /* Imperial */ string "Harpy", /* Imperial */ string "Harpy", string "Fairy", string "Bat", string "Griffin", /* Imperial */ string "Griffin", string "Angel", string "Gargoyle", string "Monk", /* Light */ string "Barbarian", /* Imperial */ string "Crusader", /* Light */ string "Crusader", string "Barbarian", /* Imperial */ string "Barbarian", string "Bandit", /* Imperial */ string "Bandit", string "Zombie", string "Skeleton", string "Wolfman", string "Ogre", string "Gel", string "Elemental", string "Civilian", string "Valkyrie", string "Freyja", string "White Dragon", string "Salamander", string "Iron Golem", string "Demon Lord", string "Sleipnir", string "Fenrir", string "Aniki", string "Bodybuilder", string "Knight Master", /* Leon */ string "Emperor", /* Rohga */ string "Zauberer", /* Egbert */ string "Assassin", string "Dark Princess", /* Lana */ string "Wizard", /* Lana */ string "Serpent Knight", /* Lester */ string "Dragon Knight", /* Keith */ string "Dragon Lord", /* Keith */ string "Noble", /* Eliza */ string "Dragon Knight", /* Cherie */ string "Royal Guard", /* Imelda */ string "Archmage", /* Jessica */ string "High Lord", /* Aaron */ string "Sword Master", /* Aaron */ string "Knight", /* Imperial */ string "Serpent Lord", /* Lester */ string "Knight Master", /* Scott */ string "Assassin", /* Imperial */ string "Ranger", /* Rohga */ string "High Master", /* Rohga */ string "Archmage", /* Sonya */ string "Wizard", /* Sonya */ string "High Priest", /* Liana */ string "Chaos", string "Bishop", string "Lushiris", string "Sorcerer", /* Imperial */ string "Silver Knight", /* Imperial */ string "Necromancer", /* Imperial */ string "Saint", /* Imperial */ string "Mage", /* Imperial */ string "Swordsman", /* Imperial */ string "Highlander", /* Imperial */ string "Saint", /* Imperial */ string "Summoner", /* Imperial */ string "Swordsman", /* Light */ string "Serpent Knight", /* Light */ string "Priest", string "Hawk Knight", string "Dragon Lord", /* Chaos */ string "Sage", /* Chaos */ string "Sage", /* Chaos */ string "Archmage", /* Imperial */ string "General", /* Imperial */ string "Sword Master", /* Chaos */ string "Serpent Lord", /* Imperial */ string "Sword Master", /* Light */ string "High Priest", string "Sage", /* Cherie */ string "General", /* Vargas */ string "Knight Master", /* Imperial */ string "Pirate", /* Imperial */ string "Knight" /* Light */ } static array SPELL_NAMES { string "Magic Arrow", string "Blast", string "Thunder", string "Fireball", string "Meteor", string "Blizzard", string "Tornado", string "Turn Undead", string "Earthquake", string "Heal 1", string "Heal 2", string "Force Heal 1", string "Force Heal 2", string "Sleep", string "Mute", string "Protection 1", string "Protection 2", string "Attack 1", string "Attack 2", string "Zone", string "Teleport", string "Resist", string "Charm", string "Quick", string "Again", string "Decrease", string "Valkyrie", string "Freyja", string "White Dragon", string "Salamander", string "Iron Golem", string "Demon Lord" } static array SUMMON_NAMES { string "Valkyrie", string "Freyja", string "White Dragon", string "Salamander", string "Iron Golem", string "Demon Lord", string "Sleipnir", string "Fenrir", string "Aniki", string "Aniki" /* Enemy? */ } -------------------------------------------------------------------------------